/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief ioport driver for Linux from ASRobo
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 */
#ifndef _IOPORTSTD_HPP_
#define _IOPORTSTD_HPP_

#include "owndebug.h"
#include "ASRoboBus.hpp"

#include <inttypes.h>
#include <string>
#include <vector>


/** 
 * Structure describing ioport permissions.
 */
struct SPermissionEntry {
  SPermissionEntry(unsigned long aFrom=0, unsigned long aNum=0, int aTurn_on=0)
    : from(aFrom),
      num(aNum),
      turn_on(aTurn_on) {}
  bool operator == (const SPermissionEntry &pe) {
    bool result = (from > pe.from && from < pe.from+pe.num) ||
      (pe.from > from && pe.from < from+num);
    if (result) {
      dPrint(10, "Intersecting entries: 0x%03x-0x%03x and 0x%03x-0x%03x",
	     from, from+num, pe.from, pe.from+num);
    }
    return result;
  }
  
  unsigned long from;  ///< Port range begin
  unsigned long num;   ///< Port count to effect
  int turn_on;         ///< Value to set (ioperm() specific)
};
  

/**
 * IOPort BUS implementation.
 * ASRobo Compatibe IOPort BUS implementation for Linux. This class
 * implements a C++ wrapper over standard ioport handling functions
 * providing standard interface for drivers.
 */
class CIOPortStd : public CASRoboBus
{
public:
  /**
   * Default constructor
   */
  CIOPortStd();
  
  /**
   * Destructor.
   */
  virtual ~CIOPortStd();
  
  /**
   * Specialized interface for the IOPORT driver. (This is because
   * the standard read/write doesn't fit very well here.
   *
   * @todo More elaborate documentation :)
   */
  int Inb(uint16_t aAddress);
  int Outb(uint8_t aByte, uint16_t aAddress);
  
  int Inw(uint16_t aAddress);
  int Outw(uint16_t aWord, uint16_t aAddress);
  
  /**
   * Binds the module to specific IO port, all Read/Write...etc. will
   * be subsequent writes to that port. Per default
   * @todo Document!
   */
  bool BindAddr(uint16_t aAddress);
  

  /**
   * Set the active IOport range for this driver instance.
   * @param[in] from            Range begin
   * @param[in] num             Number of ports to apply
   * @param[in] turn_on         Value to set the effected ports 
   *                            (ioperm() specific value)
   * @return                    true - on success, false - on failure
   */
  bool SetRange(unsigned long from, unsigned long num, int turn_on=1);
  
  
  /**
   * Derived function implementing the real opening of the device.
   *
   * @return                KBusStatusOK on ok, KBusStatusError on error.
   */
  EBusStatus Open(void);
  
  
  /**
   * Read a buffer of values from currently selected IOport address.
   *
   * @param buffer          Buffer to read data to, must be atleast 'readb' bytes.
   * @param buffer_size     Storage available
   * @return                Negative on error, otherwise the number of bytes
   *                        stored on the 'buffer'
   */
  int Read(unsigned char *buffer, int buffer_size);
    
    
  /**
   * Write a buffer of values to currently selected IOport address.
   *
   * @param buffer          Buffer to write data from. Must be atleast
   *                        'buffer_size' bytes long.
   * @param buffer_size     Lenght of the data to write.
   * @return                Negative on error, otherwise number of bytes
   *                        really sent
   */
  int Write(const unsigned char *buffer, int buffer_size);
    

  /**
   * WaitToRead, matches select(). See definition from 'ASRoboBus.hpp'
   *
   */
  EBusStatus WaitToRead(const int aTimeout_ms = -1);
    
    
  /**
   * WaitToWrite, matches select(). See definition from 'ASRoboBus.hpp'
   *
   */
  EBusStatus WaitToWrite(const int aTimeout_ms = -1);
    
    
  /**
   * Close the IOPort driver.
   *
   * @return                KBusStatusOK on ok, KBusStatusError on error.
   */
  EBusStatus Close(void);
    
private:
  SPermissionEntry iPermissions; ///< Module specific permissions
  std::vector<SPermissionEntry> iPermissionsArray; ///< Global permissions
  int iBoundAddress; ///< Current bound IO port (all std reads/writes done here)
  volatile bool iIsOpen;
  int Ioperm(unsigned long from, unsigned long num, int turn_on=1);
    
    
  CIOPortStd(const CIOPortStd &)
    :
    iPermissions(),
    iPermissionsArray(),
    iBoundAddress(),
    iIsOpen(false) {}
  
  CIOPortStd& operator=(const CIOPortStd &) { return *this; }
    
};


#endif //_IOPORTSTD_HPP_

